package main

/*
go语言原生就支持协程  高并发是它的特性

1.12版本之后差不多稳定

一个线程之上是可以运行很多协程

线程：操作系统调度
协程：用户态，用户调度

网络高并发编程模型：协程 + epoll + 非阻塞的网络fd

go --- 底层实现了协程调度
基于线程模型上实现协程调度

Go1.0的协程是G-M模型

G指Goroutine，本质上是轻量级线程，包括了调用栈，重要的调度信息，例如channel
M指Machine，一个M关联一个内核OS线程，由操作系统管理。

G-P-M模型
新的协程调度器引入了P(Processor)，成为了完善的GPM模型。Processor，它包含了运行goroutine的资源，
如果线程想运行goroutine，必须先获取P，P中还包含了可运行的G队列。

如何在一个多核心系统上尽量合理分配G，提高并发能力？
go运行时系统会始终保证至少有一个活跃的M和P去绑定各种G队列去处理相应的任务，一般这个活跃的M就称为自旋的M
查找G队列的顺序：自己的P队列 --》全局队列 --》其他P队列
按照这个顺序去查找响应的G任务，如果自己队列里没有G任务，就去全局队列找G任务处理
如果全局队列没有G任务那就去找其他P队列的任务（偷一半任务 work stealing机制），只要还有任务在执行，保证M+P不会空闲

如果某个M在执行G的过程中被系统调用阻塞了，怎么办？  --hand off机制
当M被内核调度器调度出cpu并处于阻塞状态，与该M关联的G就没有办法运行了。
go运行的时候会有一个系统监控（sysmon线程）能够监控到这种情况，就会让P与M剥离，寻找其他空闲的M或者新建M来接管这个P
然后继续运行队列中的G。原本的M从阻塞状态恢复之后：
1、如果有空闲的P，则获取一个P，继续执行原来阻塞的G
2、如果没有空闲的P，就将原来阻塞的G加入全局队列，等待被其他的P调度。线程M就进入缓存池睡眠。

#如果一个G在M上运行时间过长，怎么办？
sysmon线程监控，如果G运行时间过长，防止其他G被饿死的情况，会有一个超时时间
一个goroutine最多占用cpu 10ms，超过时间让运行的G自己主动让出M的执行权

 */

/*
G goroutine
P processer
M machine 线程

 */